package com.dkha.server.aspect;

import com.dkha.common.exception.DkException;
import com.dkha.common.result.CommonResult;
import com.dkha.common.systemcode.SystemCode;
import com.dkha.common.util.UtilValidate;
import com.dkha.server.annotation.NoRepeatSubmitAnnotation;
import com.dkha.server.system.modules.security.user.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.manager.util.SessionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;

/**
 * @version V1.0
 * @Description: 防止重复提交的切面
 * @Title:
 * @author: huangyugang
 * @date: 2019/12/23 14:01
 * @Copyright: 成都电科慧安
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect  {

    /** lua */
    private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Pointcut("@annotation(com.dkha.server.annotation.NoRepeatSubmitAnnotation)")
    public void pointCut(){}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        log.info("接口幂等性验证：");
        // 可以根据业务获取用户唯一的个人信息，例如手机号码
        if(UtilValidate.isEmpty(SecurityUser.getUserId()))
        {
            throw new DkException(SystemCode.TOKEN_ERROR);
        }
        String phone = SecurityUser.getUserId().toString();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmitAnnotation cacheLock = method.getAnnotation(NoRepeatSubmitAnnotation.class);
        String prefix = cacheLock.prefix();
        if (StringUtils.isBlank(prefix)){
            throw new DkException("CacheLock prefix can't be null");
        }
        // 拼接 key
        String delimiter = cacheLock.delimiter();
        StringBuilder sb = new StringBuilder();
        sb.append(prefix).append(delimiter).append(phone);
        final String lockKey = sb.toString();
        final String UUID = java.util.UUID.randomUUID().toString();
        try {
            // 获取锁
            boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey,UUID,cacheLock.expire(),cacheLock.timeUnit());
            if (!success){

                throw new DkException("请勿重复提交");
            }
            Object result= joinPoint.proceed();
            return result;
        }finally {
            // 最后记得释放锁
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT,Long.class);
            Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),UUID);
        }

    }

}
